{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Choose your own adventure\n",
    "\n",
    "A simple text-based game in the style of 80s adventure games in which you play a hungry corgi."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-12-13T21:54:32.774619Z",
     "start_time": "2024-12-13T21:54:31.058878Z"
    }
   },
   "source": [
    "import json\n",
    "from typing import Optional, Tuple\n",
    "\n",
    "from burr.core import action, Application, ApplicationBuilder, State, default, when\n",
    "from openai import OpenAI"
   ],
   "outputs": [],
   "execution_count": 1
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# set your OpenAI API key to run the rest of the notebook\n",
    "import os\n",
    "os.environ[\"OPENAI_API_KEY\"] = ..."
   ]
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-12-13T21:59:57.226911Z",
     "start_time": "2024-12-13T21:59:57.223370Z"
    }
   },
   "cell_type": "code",
   "source": [
    "RESTRICTIONS = \"\"\"You're a small corgi with short legs. You can't jump high,\n",
    "you can't run fast, you can't perform feats of athleticism in general\n",
    "to achieve any of your goals. You can't open doors, you can't use tools,\n",
    "you can't communicate with humans, you can't use your paws to manipulate\n",
    "objects, you can't use your mouth to communicate with humans\"\"\""
   ],
   "outputs": [],
   "execution_count": 11
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define actions"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-12-13T21:59:58.536899Z",
     "start_time": "2024-12-13T21:59:58.527089Z"
    }
   },
   "source": [
    "\n",
    "@action(reads=[\"challenges\"], writes=[\"current_challenge\"])\n",
    "def start(state: State) -> State:\n",
    "    \"\"\"Load the first challenge as the current challenge\"\"\"\n",
    "    current_challenge = state[\"challenges\"][0]\n",
    "    return state.update(current_challenge=current_challenge)\n",
    "\n",
    "\n",
    "@action(reads=[\"current_challenge\"], writes=[\"attempts\"])\n",
    "def prompt_for_challenge(state: State) -> State:\n",
    "    \"\"\"Request user input to solve the current challenge\"\"\"\n",
    "    current_challenge = state[\"current_challenge\"]\n",
    "    user_response = input(f'{current_challenge}. What do you do?\\n $ ')\n",
    "    return state.append(attempts=user_response)\n",
    "\n",
    "\n",
    "@action(\n",
    "    reads=[\"attempts\", \"current_challenge\"],\n",
    "    writes=[\"challenge_solved\", \"what_happened\"],\n",
    ")\n",
    "def evaluate_attempt(state: State) -> State:\n",
    "    response = OpenAI().chat.completions.create(\n",
    "        model=\"gpt-4o-mini\",\n",
    "        messages=[\n",
    "            {\n",
    "                \"role\": \"system\",\n",
    "                \"content\": f\"\"\"You are evaluating responses for\n",
    "             whether they constitute solutions to the provided challenge in a text\n",
    "             based game, whose protagonist is a dog subject to the following limitations:\n",
    "             {RESTRICTIONS}. You respond ONLY with a json object containing two fields: \"solved\", which is a\n",
    "             boolean indicating whether the challenge was solved by the attempt, and \"what_happened\",\n",
    "             which is a string containing a brief narrative, written in the second person and addressed\n",
    "             to the player, of what happened during the protagonist's attempt\"\"\",\n",
    "            },\n",
    "            {\n",
    "                \"role\": \"user\",\n",
    "                \"content\": f\"The current challenge is: {state['current_challenge']} \"\n",
    "                f\"and the player's attempt is: {state['attempts'][-1]}\",\n",
    "            },\n",
    "        ],\n",
    "    )\n",
    "    content = response.choices[0].message.content\n",
    "    try:\n",
    "        json_result = json.loads(content)\n",
    "    except json.JSONDecodeError:\n",
    "        print(\"bad json: \", content)\n",
    "        json_result = {\n",
    "            \"solved\": False,\n",
    "            \"what_happened\": \"Not sure, really. I'm a dog. I can't read json. I can't read at all.\",\n",
    "        }\n",
    "\n",
    "    return state.update(\n",
    "        challenge_solved=json_result[\"solved\"],\n",
    "        what_happened=json_result[\"what_happened\"],\n",
    "    )\n",
    "\n",
    "\n",
    "@action(\n",
    "    reads=[\"challenge_solved\", \"current_challenge\", \"what_happened\"],\n",
    "    writes=[\"current_challenge\", \"did_win\"],\n",
    ")\n",
    "def maybe_progress(state: State) -> State:\n",
    "    print(\"What happened:\", state[\"what_happened\"])\n",
    "    if state[\"challenge_solved\"]:\n",
    "        if state[\"current_challenge\"] == challenges[-1]:\n",
    "            result = {\"did_win\": True}\n",
    "        else:\n",
    "            result = {\n",
    "                \"current_challenge\": challenges[challenges.index(state[\"current_challenge\"]) + 1]\n",
    "            }\n",
    "    else:\n",
    "        result = {\"current_challenge\": state[\"current_challenge\"]}\n",
    "    return state.update(**result)\n",
    "\n",
    "\n",
    "@action(reads=[\"challenges\"], writes=[])\n",
    "def win(state: State) -> State:\n",
    "    # get summary of actions taken from openai\n",
    "    print(\"you won\")\n",
    "    return state\n"
   ],
   "outputs": [],
   "execution_count": 12
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-12-13T21:59:59.217332Z",
     "start_time": "2024-12-13T21:59:59.214329Z"
    }
   },
   "source": [
    "challenges = [\n",
    "    \"There is a dish of dog food on the floor. You want to eat it\",\n",
    "    \"There is a dish of dog food on a table. You want to eat it\",\n",
    "    \"There is a dish of dog food in a locked car. You want to eat it\",\n",
    "]"
   ],
   "outputs": [],
   "execution_count": 13
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-12-13T22:00:01.429752Z",
     "start_time": "2024-12-13T22:00:00.812349Z"
    }
   },
   "source": [
    "def build_application(\n",
    "    app_id: Optional[str] = None,\n",
    "    storage_dir: Optional[str] = \"~/.burr\",\n",
    ") -> Application:\n",
    "    return (\n",
    "        ApplicationBuilder()\n",
    "        .with_state(did_win=False, challenges=challenges)\n",
    "        .with_actions(\n",
    "            start=start,\n",
    "            prompt_for_challenge=prompt_for_challenge,\n",
    "            evaluate_attempt=evaluate_attempt,\n",
    "            maybe_progress=maybe_progress,\n",
    "            win=win,\n",
    "        )\n",
    "        .with_transitions(\n",
    "            (\"start\", \"prompt_for_challenge\", default),\n",
    "            (\"prompt_for_challenge\", \"evaluate_attempt\", default),\n",
    "            (\"evaluate_attempt\", \"maybe_progress\", default),\n",
    "            (\"maybe_progress\", \"win\", when(did_win=True)),\n",
    "            (\"maybe_progress\", \"prompt_for_challenge\", default),\n",
    "        )\n",
    "        .with_entrypoint(\"start\")\n",
    "        .with_tracker(project=\"demo_corgi_adventure\", params={\"storage_dir\": storage_dir})\n",
    "        .with_identifiers(app_id=app_id)\n",
    "        .build()\n",
    "    )\n",
    "\n",
    "app = build_application()\n",
    "app.visualize(output_file_path=\"digraph\", include_conditions=True, view=False, format=\"png\")"
   ],
   "outputs": [
    {
     "data": {
      "image/svg+xml": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<!-- Generated by graphviz version 12.0.0 (20240704.0754)\n -->\n<!-- Pages: 1 -->\n<svg width=\"194pt\" height=\"331pt\"\n viewBox=\"0.00 0.00 193.93 330.50\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 326.5)\">\n<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-326.5 189.93,-326.5 189.93,4 -4,4\"/>\n<!-- start -->\n<g id=\"node1\" class=\"node\">\n<title>start</title>\n<path fill=\"#b4d8e4\" stroke=\"black\" d=\"M122.93,-322.5C122.93,-322.5 92.92,-322.5 92.92,-322.5 86.92,-322.5 80.92,-316.5 80.92,-310.5 80.92,-310.5 80.92,-297.9 80.92,-297.9 80.92,-291.9 86.92,-285.9 92.92,-285.9 92.92,-285.9 122.93,-285.9 122.93,-285.9 128.93,-285.9 134.93,-291.9 134.93,-297.9 134.93,-297.9 134.93,-310.5 134.93,-310.5 134.93,-316.5 128.93,-322.5 122.93,-322.5\"/>\n<text text-anchor=\"middle\" x=\"107.92\" y=\"-298.4\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">start</text>\n</g>\n<!-- prompt_for_challenge -->\n<g id=\"node2\" class=\"node\">\n<title>prompt_for_challenge</title>\n<path fill=\"#b4d8e4\" stroke=\"black\" d=\"M172.35,-254.9C172.35,-254.9 43.5,-254.9 43.5,-254.9 37.5,-254.9 31.5,-248.9 31.5,-242.9 31.5,-242.9 31.5,-230.3 31.5,-230.3 31.5,-224.3 37.5,-218.3 43.5,-218.3 43.5,-218.3 172.35,-218.3 172.35,-218.3 178.35,-218.3 184.35,-224.3 184.35,-230.3 184.35,-230.3 184.35,-242.9 184.35,-242.9 184.35,-248.9 178.35,-254.9 172.35,-254.9\"/>\n<text text-anchor=\"middle\" x=\"107.92\" y=\"-230.8\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">prompt_for_challenge</text>\n</g>\n<!-- start&#45;&gt;prompt_for_challenge -->\n<g id=\"edge1\" class=\"edge\">\n<title>start&#45;&gt;prompt_for_challenge</title>\n<path fill=\"none\" stroke=\"black\" d=\"M107.92,-285.62C107.92,-279.74 107.92,-273.02 107.92,-266.5\"/>\n<polygon fill=\"black\" stroke=\"black\" points=\"111.43,-266.74 107.93,-256.74 104.43,-266.74 111.43,-266.74\"/>\n</g>\n<!-- evaluate_attempt -->\n<g id=\"node3\" class=\"node\">\n<title>evaluate_attempt</title>\n<path fill=\"#b4d8e4\" stroke=\"black\" d=\"M113.85,-187.3C113.85,-187.3 12,-187.3 12,-187.3 6,-187.3 0,-181.3 0,-175.3 0,-175.3 0,-162.7 0,-162.7 0,-156.7 6,-150.7 12,-150.7 12,-150.7 113.85,-150.7 113.85,-150.7 119.85,-150.7 125.85,-156.7 125.85,-162.7 125.85,-162.7 125.85,-175.3 125.85,-175.3 125.85,-181.3 119.85,-187.3 113.85,-187.3\"/>\n<text text-anchor=\"middle\" x=\"62.92\" y=\"-163.2\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">evaluate_attempt</text>\n</g>\n<!-- prompt_for_challenge&#45;&gt;evaluate_attempt -->\n<g id=\"edge2\" class=\"edge\">\n<title>prompt_for_challenge&#45;&gt;evaluate_attempt</title>\n<path fill=\"none\" stroke=\"black\" d=\"M95.87,-218.02C91.43,-211.55 86.29,-204.06 81.42,-196.96\"/>\n<polygon fill=\"black\" stroke=\"black\" points=\"84.42,-195.14 75.87,-188.88 78.64,-199.1 84.42,-195.14\"/>\n</g>\n<!-- maybe_progress -->\n<g id=\"node4\" class=\"node\">\n<title>maybe_progress</title>\n<path fill=\"#b4d8e4\" stroke=\"black\" d=\"M157.35,-119.7C157.35,-119.7 58.5,-119.7 58.5,-119.7 52.5,-119.7 46.5,-113.7 46.5,-107.7 46.5,-107.7 46.5,-95.1 46.5,-95.1 46.5,-89.1 52.5,-83.1 58.5,-83.1 58.5,-83.1 157.35,-83.1 157.35,-83.1 163.35,-83.1 169.35,-89.1 169.35,-95.1 169.35,-95.1 169.35,-107.7 169.35,-107.7 169.35,-113.7 163.35,-119.7 157.35,-119.7\"/>\n<text text-anchor=\"middle\" x=\"107.92\" y=\"-95.6\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">maybe_progress</text>\n</g>\n<!-- evaluate_attempt&#45;&gt;maybe_progress -->\n<g id=\"edge3\" class=\"edge\">\n<title>evaluate_attempt&#45;&gt;maybe_progress</title>\n<path fill=\"none\" stroke=\"black\" d=\"M74.98,-150.42C79.42,-143.95 84.56,-136.46 89.43,-129.36\"/>\n<polygon fill=\"black\" stroke=\"black\" points=\"92.21,-131.5 94.98,-121.28 86.43,-127.54 92.21,-131.5\"/>\n</g>\n<!-- maybe_progress&#45;&gt;prompt_for_challenge -->\n<g id=\"edge5\" class=\"edge\">\n<title>maybe_progress&#45;&gt;prompt_for_challenge</title>\n<path fill=\"none\" stroke=\"black\" d=\"M120.51,-119.81C126.11,-128.61 132.07,-139.73 134.93,-150.7 139.03,-166.44 139.03,-171.56 134.93,-187.3 133.05,-194.5 129.84,-201.76 126.27,-208.39\"/>\n<polygon fill=\"black\" stroke=\"black\" points=\"123.33,-206.49 121.28,-216.88 129.36,-210.04 123.33,-206.49\"/>\n</g>\n<!-- win -->\n<g id=\"node5\" class=\"node\">\n<title>win</title>\n<path fill=\"#b4d8e4\" stroke=\"black\" d=\"M122.93,-36.6C122.93,-36.6 92.92,-36.6 92.92,-36.6 86.92,-36.6 80.92,-30.6 80.92,-24.6 80.92,-24.6 80.92,-12 80.92,-12 80.92,-6 86.92,0 92.92,0 92.92,0 122.93,0 122.93,0 128.93,0 134.93,-6 134.93,-12 134.93,-12 134.93,-24.6 134.93,-24.6 134.93,-30.6 128.93,-36.6 122.93,-36.6\"/>\n<text text-anchor=\"middle\" x=\"107.92\" y=\"-12.5\" font-family=\"Helvetica,sans-Serif\" font-size=\"14.00\">win</text>\n</g>\n<!-- maybe_progress&#45;&gt;win -->\n<g id=\"edge4\" class=\"edge\">\n<title>maybe_progress&#45;&gt;win</title>\n<path fill=\"none\" stroke=\"black\" stroke-dasharray=\"5,2\" d=\"M107.92,-82.8C107.92,-72.71 107.92,-59.78 107.92,-48.23\"/>\n<polygon fill=\"black\" stroke=\"black\" points=\"111.43,-48.38 107.93,-38.38 104.43,-48.38 111.43,-48.38\"/>\n<text text-anchor=\"middle\" x=\"146.93\" y=\"-54.8\" font-family=\"Times,serif\" font-size=\"14.00\">did_win=True</text>\n</g>\n</g>\n</svg>\n",
      "text/plain": [
       "<graphviz.graphs.Digraph at 0x1264f80a0>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 14
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-12-13T22:01:14.166318Z",
     "start_time": "2024-12-13T22:00:01.585581Z"
    }
   },
   "source": [
    "action, state, result = app.run(halt_after=[\"win\"])"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "What happened: You opened your mouth and attempted to eat the food, but without the ability to manipulate the dish, you couldn’t reach the food. It remained untouched on the floor, taunting you with its delicious aroma.\n",
      "What happened: You tugged at the dish with your mouth, but your short legs can't provide enough strength to pull it any closer. The dish remains out of reach, and your belly rumbles in disappointment as you realize you can't manipulate it like you hoped.\n",
      "What happened: You approach the dish of dog food with eager anticipation, but as you try to lean over and take a bite, your short legs make it difficult to reach the food comfortably. You can't quite get your mouth to the dish, and after a few attempts, you realize you'll need a different plan to enjoy your meal.\n",
      "What happened: You try to bite the side of the dish to tip it over, but your short legs and limited strength make it impossible. The dish remains firmly on the floor, and the tantalizing smell of the dog food wafts up to you, teasing you further.\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "Interrupted by user",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31mKeyboardInterrupt\u001B[0m                         Traceback (most recent call last)",
      "Cell \u001B[0;32mIn[15], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m action, state, result \u001B[38;5;241m=\u001B[39m \u001B[43mapp\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mrun\u001B[49m\u001B[43m(\u001B[49m\u001B[43mhalt_after\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m[\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mwin\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m]\u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[0;32m~/dagworks/burr/burr/telemetry.py:276\u001B[0m, in \u001B[0;36mcapture_function_usage.<locals>.wrapped_fn\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m    273\u001B[0m \u001B[38;5;129m@functools\u001B[39m\u001B[38;5;241m.\u001B[39mwraps(call_fn)\n\u001B[1;32m    274\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mwrapped_fn\u001B[39m(\u001B[38;5;241m*\u001B[39margs, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs):\n\u001B[1;32m    275\u001B[0m     \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m--> 276\u001B[0m         \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mcall_fn\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m    277\u001B[0m     \u001B[38;5;28;01mfinally\u001B[39;00m:\n\u001B[1;32m    278\u001B[0m         \u001B[38;5;28;01mif\u001B[39;00m is_telemetry_enabled():\n",
      "File \u001B[0;32m~/dagworks/burr/burr/core/application.py:672\u001B[0m, in \u001B[0;36m_call_execute_method_pre_post.__call__.<locals>.wrapper_sync\u001B[0;34m(app_self, *args, **kwargs)\u001B[0m\n\u001B[1;32m    670\u001B[0m exc \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[1;32m    671\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m--> 672\u001B[0m     \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mfn\u001B[49m\u001B[43m(\u001B[49m\u001B[43mapp_self\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m    673\u001B[0m \u001B[38;5;28;01mfinally\u001B[39;00m:\n\u001B[1;32m    674\u001B[0m     \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mcall_post(app_self, exc)\n",
      "File \u001B[0;32m~/dagworks/burr/burr/core/application.py:1243\u001B[0m, in \u001B[0;36mApplication.run\u001B[0;34m(self, halt_before, halt_after, inputs)\u001B[0m\n\u001B[1;32m   1241\u001B[0m \u001B[38;5;28;01mwhile\u001B[39;00m \u001B[38;5;28;01mTrue\u001B[39;00m:\n\u001B[1;32m   1242\u001B[0m     \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m-> 1243\u001B[0m         \u001B[38;5;28;43mnext\u001B[39;49m\u001B[43m(\u001B[49m\u001B[43mgen\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m   1244\u001B[0m     \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mStopIteration\u001B[39;00m \u001B[38;5;28;01mas\u001B[39;00m e:\n\u001B[1;32m   1245\u001B[0m         result \u001B[38;5;241m=\u001B[39m e\u001B[38;5;241m.\u001B[39mvalue\n",
      "File \u001B[0;32m~/dagworks/burr/burr/core/application.py:1186\u001B[0m, in \u001B[0;36mApplication.iterate\u001B[0;34m(self, halt_before, halt_after, inputs)\u001B[0m\n\u001B[1;32m   1183\u001B[0m prior_action: Optional[Action] \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[1;32m   1184\u001B[0m \u001B[38;5;28;01mwhile\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mhas_next_action():\n\u001B[1;32m   1185\u001B[0m     \u001B[38;5;66;03m# self.step will only return None if there is no next action, so we can rely on tuple unpacking\u001B[39;00m\n\u001B[0;32m-> 1186\u001B[0m     prior_action, result, state \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mstep\u001B[49m\u001B[43m(\u001B[49m\u001B[43minputs\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43minputs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m   1187\u001B[0m     \u001B[38;5;28;01myield\u001B[39;00m prior_action, result, state\n\u001B[1;32m   1188\u001B[0m     \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_should_halt_iterate(halt_before, halt_after, prior_action):\n",
      "File \u001B[0;32m~/dagworks/burr/burr/core/application.py:672\u001B[0m, in \u001B[0;36m_call_execute_method_pre_post.__call__.<locals>.wrapper_sync\u001B[0;34m(app_self, *args, **kwargs)\u001B[0m\n\u001B[1;32m    670\u001B[0m exc \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[1;32m    671\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m--> 672\u001B[0m     \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mfn\u001B[49m\u001B[43m(\u001B[49m\u001B[43mapp_self\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m    673\u001B[0m \u001B[38;5;28;01mfinally\u001B[39;00m:\n\u001B[1;32m    674\u001B[0m     \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mcall_post(app_self, exc)\n",
      "File \u001B[0;32m~/dagworks/burr/burr/core/application.py:843\u001B[0m, in \u001B[0;36mApplication.step\u001B[0;34m(self, inputs)\u001B[0m\n\u001B[1;32m    840\u001B[0m \u001B[38;5;66;03m# we need to increment the sequence before we start computing\u001B[39;00m\n\u001B[1;32m    841\u001B[0m \u001B[38;5;66;03m# that way if we're replaying from state, we don't get stuck\u001B[39;00m\n\u001B[1;32m    842\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_increment_sequence_id()\n\u001B[0;32m--> 843\u001B[0m out \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_step\u001B[49m\u001B[43m(\u001B[49m\u001B[43minputs\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43minputs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43m_run_hooks\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mTrue\u001B[39;49;00m\u001B[43m)\u001B[49m\n\u001B[1;32m    844\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m out\n",
      "File \u001B[0;32m~/dagworks/burr/burr/core/application.py:885\u001B[0m, in \u001B[0;36mApplication._step\u001B[0;34m(self, inputs, _run_hooks)\u001B[0m\n\u001B[1;32m    883\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m    884\u001B[0m     \u001B[38;5;28;01mif\u001B[39;00m next_action\u001B[38;5;241m.\u001B[39msingle_step:\n\u001B[0;32m--> 885\u001B[0m         result, new_state \u001B[38;5;241m=\u001B[39m \u001B[43m_run_single_step_action\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m    886\u001B[0m \u001B[43m            \u001B[49m\u001B[43mnext_action\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_state\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43maction_inputs\u001B[49m\n\u001B[1;32m    887\u001B[0m \u001B[43m        \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m    888\u001B[0m     \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[1;32m    889\u001B[0m         result \u001B[38;5;241m=\u001B[39m _run_function(\n\u001B[1;32m    890\u001B[0m             next_action, \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_state, action_inputs, name\u001B[38;5;241m=\u001B[39mnext_action\u001B[38;5;241m.\u001B[39mname\n\u001B[1;32m    891\u001B[0m         )\n",
      "File \u001B[0;32m~/dagworks/burr/burr/core/application.py:287\u001B[0m, in \u001B[0;36m_run_single_step_action\u001B[0;34m(action, state, inputs)\u001B[0m\n\u001B[1;32m    284\u001B[0m \u001B[38;5;66;03m# TODO -- guard all reads/writes with a subset of the state\u001B[39;00m\n\u001B[1;32m    285\u001B[0m action\u001B[38;5;241m.\u001B[39mvalidate_inputs(inputs)\n\u001B[1;32m    286\u001B[0m result, new_state \u001B[38;5;241m=\u001B[39m _adjust_single_step_output(\n\u001B[0;32m--> 287\u001B[0m     \u001B[43maction\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mrun_and_update\u001B[49m\u001B[43m(\u001B[49m\u001B[43mstate\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43minputs\u001B[49m\u001B[43m)\u001B[49m, action\u001B[38;5;241m.\u001B[39mname, action\u001B[38;5;241m.\u001B[39mschema\n\u001B[1;32m    288\u001B[0m )\n\u001B[1;32m    289\u001B[0m _validate_result(result, action\u001B[38;5;241m.\u001B[39mname, action\u001B[38;5;241m.\u001B[39mschema)\n\u001B[1;32m    290\u001B[0m out \u001B[38;5;241m=\u001B[39m result, _state_update(state, new_state)\n",
      "File \u001B[0;32m~/dagworks/burr/burr/core/action.py:655\u001B[0m, in \u001B[0;36mFunctionBasedAction.run_and_update\u001B[0;34m(self, state, **run_kwargs)\u001B[0m\n\u001B[1;32m    654\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mrun_and_update\u001B[39m(\u001B[38;5;28mself\u001B[39m, state: State, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mrun_kwargs) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m \u001B[38;5;28mtuple\u001B[39m[\u001B[38;5;28mdict\u001B[39m, State]:\n\u001B[0;32m--> 655\u001B[0m     \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_fn\u001B[49m\u001B[43m(\u001B[49m\u001B[43mstate\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_bound_params\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mrun_kwargs\u001B[49m\u001B[43m)\u001B[49m\n",
      "Cell \u001B[0;32mIn[12], line 12\u001B[0m, in \u001B[0;36mprompt_for_challenge\u001B[0;34m(state)\u001B[0m\n\u001B[1;32m     10\u001B[0m \u001B[38;5;250m\u001B[39m\u001B[38;5;124;03m\"\"\"Request user input to solve the current challenge\"\"\"\u001B[39;00m\n\u001B[1;32m     11\u001B[0m current_challenge \u001B[38;5;241m=\u001B[39m state[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mcurrent_challenge\u001B[39m\u001B[38;5;124m\"\u001B[39m]\n\u001B[0;32m---> 12\u001B[0m user_response \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43minput\u001B[39;49m\u001B[43m(\u001B[49m\u001B[38;5;124;43mf\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;132;43;01m{\u001B[39;49;00m\u001B[43mcurrent_challenge\u001B[49m\u001B[38;5;132;43;01m}\u001B[39;49;00m\u001B[38;5;124;43m. What do you do?\u001B[39;49m\u001B[38;5;130;43;01m\\n\u001B[39;49;00m\u001B[38;5;124;43m $ \u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m)\u001B[49m\n\u001B[1;32m     13\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m state\u001B[38;5;241m.\u001B[39mappend(attempts\u001B[38;5;241m=\u001B[39muser_response)\n",
      "File \u001B[0;32m~/.pyenv/versions/burr-py310/lib/python3.10/site-packages/ipykernel/kernelbase.py:1270\u001B[0m, in \u001B[0;36mKernel.raw_input\u001B[0;34m(self, prompt)\u001B[0m\n\u001B[1;32m   1268\u001B[0m     msg \u001B[38;5;241m=\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mraw_input was called, but this frontend does not support input requests.\u001B[39m\u001B[38;5;124m\"\u001B[39m\n\u001B[1;32m   1269\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m StdinNotImplementedError(msg)\n\u001B[0;32m-> 1270\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_input_request\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m   1271\u001B[0m \u001B[43m    \u001B[49m\u001B[38;5;28;43mstr\u001B[39;49m\u001B[43m(\u001B[49m\u001B[43mprompt\u001B[49m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m   1272\u001B[0m \u001B[43m    \u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_parent_ident\u001B[49m\u001B[43m[\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mshell\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m]\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m   1273\u001B[0m \u001B[43m    \u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_parent\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mshell\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m   1274\u001B[0m \u001B[43m    \u001B[49m\u001B[43mpassword\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m,\u001B[49m\n\u001B[1;32m   1275\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[0;32m~/.pyenv/versions/burr-py310/lib/python3.10/site-packages/ipykernel/kernelbase.py:1313\u001B[0m, in \u001B[0;36mKernel._input_request\u001B[0;34m(self, prompt, ident, parent, password)\u001B[0m\n\u001B[1;32m   1310\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mKeyboardInterrupt\u001B[39;00m:\n\u001B[1;32m   1311\u001B[0m     \u001B[38;5;66;03m# re-raise KeyboardInterrupt, to truncate traceback\u001B[39;00m\n\u001B[1;32m   1312\u001B[0m     msg \u001B[38;5;241m=\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mInterrupted by user\u001B[39m\u001B[38;5;124m\"\u001B[39m\n\u001B[0;32m-> 1313\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mKeyboardInterrupt\u001B[39;00m(msg) \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[1;32m   1314\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mException\u001B[39;00m:\n\u001B[1;32m   1315\u001B[0m     \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mlog\u001B[38;5;241m.\u001B[39mwarning(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mInvalid Message:\u001B[39m\u001B[38;5;124m\"\u001B[39m, exc_info\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mTrue\u001B[39;00m)\n",
      "\u001B[0;31mKeyboardInterrupt\u001B[0m: Interrupted by user"
     ]
    }
   ],
   "execution_count": 15
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": ""
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
